// 组合模式 -------------
/*
定义：将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
要点：
a)Composite模式采用树形结构来实现普遍存在的对象容器，从而将“一对多”的关系转化为“一对一”的关系，
使得客户代码可以一致地处理对象和对象容器，无需关心处理的是单个的对象，还是组合的对象容器。
b)将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思想，解耦之后，客户代码将与纯粹的抽象接口——而非对象容器
的复内部实现结构——发生依赖关系，从而更能“应对变化”。
c)Composite模式中，是将“Add和Remove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中，
还是将其定义在“表示对象容器的Composite类”中，是一个关乎“透明性”和“安全性”的两难问题，需要仔细权衡。
这里有可能违背面向对象的“单一职责原则”，但是对于这种特殊结构，这又是必须付出的代价。A
SP.NET控件的实现在这方面为我们提供了一个很好的示范。
d)Composite模式在具体实现中，可以让父对象中的子对象反向追溯；如果父对象有频繁的遍历需求，可使用缓存技巧来改善效率。
*/ 
class Company {
  constructor(name) {
    this.name = name
    this.children = []
  }

  add(company) {
    this.children.push(company)
  }

  remove(company) {
    this.children.splice(this.children.indexOf(company), 1)
  }

  display(level) {
    console.log('-'.repeat(level * 2) + this.name)
  }

  lineOfDuty() {
    console.log(this.name)
  }
}

class HRDepartment extends Company {
  add(company) {
    // 叶节点没有子节点
  }

  remove(company) {
    // 叶节点没有子节点
  }

  lineOfDuty() {
    console.log(this.name + "：员工招聘培训管理")
  }
}
class FinanceDepartment extends Company {
  add(company) {
    // 叶节点没有子节点
  }

  remove(company) {
    // 叶节点没有子节点
  }

  lineOfDuty() {
    console.log(this.name + "：公司财务收支管理")
  }
}

class ConcreteCompany extends Company {
  display(level) {
    super.display(level)
    this.children.forEach(c => {
      c.display(level + 1)
    })
  }

  lineOfDuty() {
    this.children.forEach(c => {
      c.lineOfDuty()
    })
  }
}

const root = new ConcreteCompany("北京总公司");
root.add(new HRDepartment("总公司人力资源部"));
root.add(new FinanceDepartment("总公司财务部"));

const shCorp = new ConcreteCompany("上海华东分公司");
shCorp.add(new HRDepartment("上海华东分公司人力资源部"));
shCorp.add(new FinanceDepartment("上海华东分公司财务部"));
root.add(shCorp);

const njCorp = new ConcreteCompany("南京办事处");
njCorp.add(new HRDepartment("南京办事处人力资源部"));
njCorp.add(new FinanceDepartment("南京办事处财务部"));
root.add(njCorp);

// console.log("结构图：");
// root.display(2);
// console.log("职责：");
// root.lineOfDuty();

console.log(root)

// root.remove(njCorp)